package com.kxmall.web.controller.coupon.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kxmall.common.enums.StatusType;
import com.kxmall.common.exception.ServiceException;
import com.kxmall.common.utils.redis.RedisUtils;
import com.kxmall.coupon.domain.KxStoreCouponIssue;
import com.kxmall.coupon.domain.KxStoreCouponUser;
import com.kxmall.coupon.domain.vo.KxStoreCouponIssueVo;
import com.kxmall.coupon.domain.vo.KxStoreCouponUserVo;
import com.kxmall.coupon.domain.vo.KxStoreCouponVo;
import com.kxmall.coupon.mapper.KxStoreCouponIssueMapper;
import com.kxmall.coupon.mapper.KxStoreCouponMapper;
import com.kxmall.coupon.mapper.KxStoreCouponUserMapper;
import com.kxmall.model.KVModel;
import com.kxmall.web.controller.coupon.service.IKxAppCouponService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

/**
 * @author kaixin
 * @version 1.0
 * @date 2023/5/3
 */
@RequiredArgsConstructor
@Service
public class KxAppCouponService implements IKxAppCouponService {

    private final KxStoreCouponMapper storeCouponMapper;

    private final KxStoreCouponUserMapper storeCouponUserMapper;

    private final KxStoreCouponIssueMapper storeCouponIssueMapper;

    private static final Logger logger = LoggerFactory.getLogger(KxAppCouponService.class);

    @Override
    public List<KxStoreCouponUserVo> getUserCoupons(Long userId, Integer status) {
        return storeCouponUserMapper.getUserCoupons(userId, status);
    }

    @Override
    public List<KxStoreCouponUserVo> getUserCouponss(Long userId, Integer status) {
        return storeCouponUserMapper.getUserCouponss(userId, status);
    }


    private static final String COUPON_LOCK = "COUPON_LOCK_";

    private static final String COUPON_USER_LOCK = "COUPON_USER_LOCK_";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean obtainCoupon(Long couponId, Long userId) {

        Lock lock = RedisUtils.lock(COUPON_USER_LOCK + userId + "_" + couponId);
        boolean isLocked;
        //防止用户一瞬间提交两次表单，导致超领
        try {
            isLocked = lock.tryLock(10, TimeUnit.SECONDS);
            if (isLocked) {
                KxStoreCouponIssue couponIssue = storeCouponIssueMapper.selectById(couponId);
                if (couponIssue.getStatus() == StatusType.LOCK.getCode()) {
                    throw new ServiceException("优惠券活动已经冻结！");
                }
                Date now = new Date();
                if (couponIssue.getEndTime() != null && couponIssue.getEndTime().getTime() < now.getTime()) {
                    throw new ServiceException("优惠券活动已经结束");
                }
                if (couponIssue.getStartTime() != null && couponIssue.getStartTime().getTime() > now.getTime()) {
                    throw new ServiceException("优惠券活动还没开始");
                }
                if (couponIssue.getTotalCount() != -1 && couponIssue.getRemainCount() <= 0) {
                    throw new ServiceException("优惠券已经领完～");
                } else {
                    if (couponIssue.getTotalCount() >= 0) {
                        if (couponIssue.getRemainCount() == 1) {
                            if (!RedisUtils.lock(COUPON_LOCK + couponId).tryLock(10, TimeUnit.SECONDS)) {
                                throw new ServiceException("优惠券已经领完～");
                            }
                        }
                        storeCouponIssueMapper.decCoupon(couponId);
                    }
                }


                if (couponIssue.getIsPermanent() != 1) {
                    //校验用户是否已经领了
                    Long count = storeCouponUserMapper.selectCount(
                            new QueryWrapper<KxStoreCouponUser>()
                                    .eq("uid", userId)
                                    .eq("ciid", couponId));

                    if (count > 0) {
                        throw new ServiceException("您已经领取过了～");
                    }
                }

                //领取优惠券
                KxStoreCouponUser userCouponDO = new KxStoreCouponUser();
                userCouponDO.setUid(userId);
                KxStoreCouponVo storeCouponVo = storeCouponMapper.selectVoById(couponIssue.getCid());
                userCouponDO.setCid(storeCouponVo.getId());
                userCouponDO.setCiid(couponId);

                userCouponDO.setCouponTitle(storeCouponVo.getTitle());
                userCouponDO.setStatus(0);
                userCouponDO.setCouponPrice(storeCouponVo.getCouponPrice());
                userCouponDO.setUseMinPrice(storeCouponVo.getUseMinPrice());
                //如果是任意领取的，则从当前时间 加上 可用天数
                userCouponDO.setEndTime(new Date(now.getTime() + 1000l * 60 * 60 * 24 * storeCouponVo.getCouponTime()));

                userCouponDO.setUpdateTime(now);
                userCouponDO.setCreateTime(now);

                storeCouponUserMapper.insert(userCouponDO);
                return true;
            } else {
                throw new ServiceException("系统繁忙~");
            }
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            logger.error("[领取优惠券] 异常", e);
            throw new ServiceException("系统未知异常");
        } finally {
            lock.unlock();
        }
    }

    @Override
    public List<KxStoreCouponIssueVo> getObtainableCoupon(Long userId) {
        List<KxStoreCouponIssueVo> couponDOS = storeCouponUserMapper.getActiveCoupons();
        //活动中的优惠券Id
        List<Long> activeCouponIds = couponDOS.stream().map(KxStoreCouponIssueVo::getId).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(activeCouponIds)) {
            return new ArrayList<>();
        }

        List<KVModel<BigInteger, Long>> userCouponsCount = storeCouponUserMapper.getUserCouponsCount(userId, activeCouponIds);

        List<KxStoreCouponIssueVo> couponDTOList = couponDOS.stream().map(item -> {
            item.setNowCount(0);
            for (int i = 0; i < userCouponsCount.size(); i++) {
                KVModel<BigInteger, Long> kv = userCouponsCount.get(i);
                if (kv != null && kv.getKey().equals(BigInteger.valueOf(item.getId()))) {
                    item.setNowCount(kv.getValue().intValue());
                }
            }
            return item;
        }).collect(Collectors.toList());
        return couponDTOList;
    }

    @Override
    public KxStoreCouponUserVo getUserCouponsById(Long id) {
        return storeCouponUserMapper.getUserCouponsById(id);
    }
}
